using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Encryption;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.Zip
{
	public class ZipInputStream : InflaterInputStream
	{
		private delegate int ReadDataHandler(byte[] byte_0, int offset, int length);
		private ZipInputStream.ReadDataHandler internalReader;
		private Crc32 crc32_0 = new Crc32();
		private ZipEntry entry;
		private long size;
		private int method;
		private int flags;
		private string password;
		public string Password
		{
			get
			{
				return this.password;
			}
			set
			{
				this.password = value;
			}
		}
		public bool CanDecompressEntry
		{
			get
			{
				return this.entry != null && this.entry.CanDecompress;
			}
		}
		public override int Available
		{
			get
			{
				return (this.entry != null) ? 1 : 0;
			}
		}
		public override long Length
		{
			get
			{
				if (this.entry == null)
				{
					throw new InvalidOperationException("No current entry");
				}
				if (this.entry.Size < 0L)
				{
					throw new ZipException("Length not available for the current entry");
				}
				return this.entry.Size;
			}
		}
		public ZipInputStream(Stream baseInputStream) : base(baseInputStream, new Inflater(true))
		{
			this.internalReader = new ZipInputStream.ReadDataHandler(this.ReadingNotAvailable);
		}
		public ZipInputStream(Stream baseInputStream, int bufferSize) : base(baseInputStream, new Inflater(true), bufferSize)
		{
			this.internalReader = new ZipInputStream.ReadDataHandler(this.ReadingNotAvailable);
		}
		public ZipEntry GetNextEntry()
		{
			if (this.crc32_0 == null)
			{
				throw new InvalidOperationException("Closed.");
			}
			if (this.entry != null)
			{
				this.CloseEntry();
			}
			int num = this.inputBuffer.ReadLeInt();
			ZipEntry result;
			if (num == 33639248 || num == 101010256 || num == 84233040 || num == 117853008 || num == 101075792)
			{
				this.Close();
				result = null;
			}
			else
			{
				if (num == 808471376 || num == 134695760)
				{
					num = this.inputBuffer.ReadLeInt();
				}
				if (num != 67324752)
				{
					throw new ZipException("Wrong Local header signature: 0x" + string.Format("{0:X}", num));
				}
				short versionRequiredToExtract = (short)this.inputBuffer.ReadLeShort();
				this.flags = this.inputBuffer.ReadLeShort();
				this.method = this.inputBuffer.ReadLeShort();
				uint num2 = (uint)this.inputBuffer.ReadLeInt();
				int num3 = this.inputBuffer.ReadLeInt();
				this.csize = (long)this.inputBuffer.ReadLeInt();
				this.size = (long)this.inputBuffer.ReadLeInt();
				int num4 = this.inputBuffer.ReadLeShort();
				int num5 = this.inputBuffer.ReadLeShort();
				bool flag = (this.flags & 1) == 1;
				byte[] array = new byte[num4];
				this.inputBuffer.ReadRawBuffer(array);
				string name = ZipConstants.ConvertToStringExt(this.flags, array);
				this.entry = new ZipEntry(name, (int)versionRequiredToExtract);
				this.entry.Flags = this.flags;
				this.entry.CompressionMethod = (CompressionMethod)this.method;
				if ((this.flags & 8) == 0)
				{
					this.entry.Crc = ((long)num3 & (long)((ulong)-1));
					this.entry.Size = (this.size & (long)((ulong)-1));
					this.entry.CompressedSize = (this.csize & (long)((ulong)-1));
					this.entry.CryptoCheckValue = (byte)(num3 >> 24 & 255);
				}
				else
				{
					if (num3 != 0)
					{
						this.entry.Crc = ((long)num3 & (long)((ulong)-1));
					}
					if (this.size != 0L)
					{
						this.entry.Size = (this.size & (long)((ulong)-1));
					}
					if (this.csize != 0L)
					{
						this.entry.CompressedSize = (this.csize & (long)((ulong)-1));
					}
					this.entry.CryptoCheckValue = (byte)(num2 >> 8 & 255u);
				}
				this.entry.DosTime = (long)((ulong)num2);
				if (num5 > 0)
				{
					byte[] array2 = new byte[num5];
					this.inputBuffer.ReadRawBuffer(array2);
					this.entry.ExtraData = array2;
				}
				this.entry.ProcessExtraData(true);
				if (this.entry.CompressedSize >= 0L)
				{
					this.csize = this.entry.CompressedSize;
				}
				if (this.entry.Size >= 0L)
				{
					this.size = this.entry.Size;
				}
				bool arg_344_0;
				if (this.method == 0)
				{
					if (!flag)
					{
						if (this.csize != this.size)
						{
							arg_344_0 = false;
							goto IL_344;
						}
					}
					arg_344_0 = (!flag || this.csize - 12L == this.size);
				}
				else
				{
					arg_344_0 = true;
				}
				IL_344:
				if (!arg_344_0)
				{
					throw new ZipException("Stored, but compressed != uncompressed");
				}
				if (this.entry.IsCompressionMethodSupported())
				{
					this.internalReader = new ZipInputStream.ReadDataHandler(this.InitialRead);
				}
				else
				{
					this.internalReader = new ZipInputStream.ReadDataHandler(this.ReadingNotSupported);
				}
				result = this.entry;
			}
			return result;
		}
		private void ReadDataDescriptor()
		{
			if (this.inputBuffer.ReadLeInt() != 134695760)
			{
				throw new ZipException("Data descriptor signature not found");
			}
			this.entry.Crc = ((long)this.inputBuffer.ReadLeInt() & (long)((ulong)-1));
			if (this.entry.LocalHeaderRequiresZip64)
			{
				this.csize = this.inputBuffer.ReadLeLong();
				this.size = this.inputBuffer.ReadLeLong();
			}
			else
			{
				this.csize = (long)this.inputBuffer.ReadLeInt();
				this.size = (long)this.inputBuffer.ReadLeInt();
			}
			this.entry.CompressedSize = this.csize;
			this.entry.Size = this.size;
		}
		private void CompleteCloseEntry(bool testCrc)
		{
			base.StopDecrypting();
			if ((this.flags & 8) != 0)
			{
				this.ReadDataDescriptor();
			}
			this.size = 0L;
			if (testCrc && (this.crc32_0.Value & (long)((ulong)-1)) != this.entry.Crc && this.entry.Crc != -1L)
			{
				throw new ZipException("CRC mismatch");
			}
			this.crc32_0.Reset();
			if (this.method == 8)
			{
				this.inflater_0.Reset();
			}
			this.entry = null;
		}
		public void CloseEntry()
		{
			if (this.crc32_0 == null)
			{
				throw new InvalidOperationException("Closed");
			}
			if (this.entry != null)
			{
				if (this.method == 8)
				{
					if ((this.flags & 8) != 0)
					{
						byte[] array = new byte[4096];
						while (this.Read(array, 0, array.Length) > 0)
						{
						}
						return;
					}
					this.csize -= this.inflater_0.TotalIn;
					this.inputBuffer.Available += this.inflater_0.RemainingInput;
				}
				if ((long)this.inputBuffer.Available > this.csize && this.csize >= 0L)
				{
					this.inputBuffer.Available = (int)((long)this.inputBuffer.Available - this.csize);
				}
				else
				{
					this.csize -= (long)this.inputBuffer.Available;
					this.inputBuffer.Available = 0;
					while (this.csize != 0L)
					{
						long num = base.Skip(this.csize);
						if (num <= 0L)
						{
							throw new ZipException("Zip archive ends early.");
						}
						this.csize -= num;
					}
				}
				this.CompleteCloseEntry(false);
			}
		}
		public override int ReadByte()
		{
			byte[] array = new byte[1];
			int result;
			if (this.Read(array, 0, 1) <= 0)
			{
				result = -1;
			}
			else
			{
				result = (int)(array[0] & 255);
			}
			return result;
		}
		private int ReadingNotAvailable(byte[] destination, int offset, int count)
		{
			throw new InvalidOperationException("Unable to read from this stream");
		}
		private int ReadingNotSupported(byte[] destination, int offset, int count)
		{
			throw new ZipException("The compression method for this entry is not supported");
		}
		private int InitialRead(byte[] destination, int offset, int count)
		{
			if (!this.CanDecompressEntry)
			{
				throw new ZipException("Library cannot extract this entry. Version required is (" + this.entry.Version.ToString() + ")");
			}
			if (this.entry.IsCrypted)
			{
				if (this.password == null)
				{
					throw new ZipException("No password set.");
				}
				PkzipClassicManaged pkzipClassicManaged = new PkzipClassicManaged();
				byte[] array = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(this.password));
				this.inputBuffer.CryptoTransform = pkzipClassicManaged.CreateDecryptor(array, null);
				byte[] array2 = new byte[12];
				this.inputBuffer.ReadClearTextBuffer(array2, 0, 12);
				if (array2[11] != this.entry.CryptoCheckValue)
				{
					throw new ZipException("Invalid password");
				}
				if (this.csize >= 12L)
				{
					this.csize -= 12L;
				}
				else
				{
					if ((this.entry.Flags & 8) == 0)
					{
						throw new ZipException(string.Format("Entry compressed size {0} too small for encryption", this.csize));
					}
				}
			}
			else
			{
				this.inputBuffer.CryptoTransform = null;
			}
			int result;
			if (this.csize > 0L || (this.flags & 8) != 0)
			{
				if (this.method == 8 && this.inputBuffer.Available > 0)
				{
					this.inputBuffer.SetInflaterInput(this.inflater_0);
				}
				this.internalReader = new ZipInputStream.ReadDataHandler(this.BodyRead);
				result = this.BodyRead(destination, offset, count);
			}
			else
			{
				this.internalReader = new ZipInputStream.ReadDataHandler(this.ReadingNotAvailable);
				result = 0;
			}
			return result;
		}
		public override int Read(byte[] buffer, int offset, int count)
		{
			if (buffer == null)
			{
				throw new ArgumentNullException("buffer");
			}
			if (offset < 0)
			{
				throw new ArgumentOutOfRangeException("offset", "Cannot be negative");
			}
			if (count < 0)
			{
				throw new ArgumentOutOfRangeException("count", "Cannot be negative");
			}
			if (buffer.Length - offset < count)
			{
				throw new ArgumentException("Invalid offset/count combination");
			}
			return this.internalReader(buffer, offset, count);
		}
		private int BodyRead(byte[] buffer, int offset, int count)
		{
			if (this.crc32_0 == null)
			{
				throw new InvalidOperationException("Closed");
			}
			int result;
			if (this.entry == null || count <= 0)
			{
				result = 0;
			}
			else
			{
				if (offset + count > buffer.Length)
				{
					throw new ArgumentException("Offset + count exceeds buffer size");
				}
				bool flag = false;
				int num = this.method;
				if (num != 0)
				{
					if (num == 8)
					{
						count = base.Read(buffer, offset, count);
						if (count <= 0)
						{
							if (!this.inflater_0.IsFinished)
							{
								throw new ZipException("Inflater not finished!");
							}
							this.inputBuffer.Available = this.inflater_0.RemainingInput;
							bool arg_F0_0;
							if ((this.flags & 8) == 0)
							{
								if (this.inflater_0.TotalIn != this.csize && this.csize != (long)((ulong)-1))
								{
									if (this.csize != -1L)
									{
										arg_F0_0 = false;
										goto IL_F0;
									}
								}
								arg_F0_0 = (this.inflater_0.TotalOut == this.size);
							}
							else
							{
								arg_F0_0 = true;
							}
							IL_F0:
							if (!arg_F0_0)
							{
								throw new ZipException(string.Concat(new object[]
								{
									"Size mismatch: ",
									this.csize,
									";",
									this.size,
									" <-> ",
									this.inflater_0.TotalIn,
									";",
									this.inflater_0.TotalOut
								}));
							}
							this.inflater_0.Reset();
							flag = true;
						}
					}
				}
				else
				{
					if ((long)count > this.csize && this.csize >= 0L)
					{
						count = (int)this.csize;
					}
					if (count > 0)
					{
						count = this.inputBuffer.ReadClearTextBuffer(buffer, offset, count);
						if (count > 0)
						{
							this.csize -= (long)count;
							this.size -= (long)count;
						}
					}
					if (this.csize == 0L)
					{
						flag = true;
					}
					else
					{
						if (count < 0)
						{
							throw new ZipException("EOF in stored block");
						}
					}
				}
				if (count > 0)
				{
					this.crc32_0.Update(buffer, offset, count);
				}
				if (flag)
				{
					this.CompleteCloseEntry(true);
				}
				result = count;
			}
			return result;
		}
		public override void Close()
		{
			this.internalReader = new ZipInputStream.ReadDataHandler(this.ReadingNotAvailable);
			this.crc32_0 = null;
			this.entry = null;
			base.Close();
		}
	}
}
